using Gdk;
using Gtk;
using irex;

public class ScibbleTest : GLib.Object, irex.Application {

    irex.IPC ipc;
    irex.Menu mainMenu;
    irex.ScribbleManager scribble;
    Gtk.Window mainWin;
    Gdk.Pixmap pixmap;
    Gtk.DrawingArea drawingArea;
    bool newscribble = false;
    int pensize = 3;
    int oldx = -1;
    int oldy = -1;
    int drawWidth;
    int drawHeight;

    /**
     * Initialise our app, create IPC connection and main menu
     */
    construct {
        ipc = new irex.IPC("drawpad", "1.0", this);
        var mm = new irex.MenuManager(ipc);
        mainMenu = new irex.Menu(mm, "mainmenu", "Menu");
        var group = mainMenu.addGroup("actions", "Actions");
        group.addItem("close", "Close", "close");
        group = mainMenu.addGroup("drawing", "Drawing");
        group.addItem("clear", "Clear Page", "cut");
        group.addItem("size1", "Small Pen", "stylus_1");
        group.addItem("size2", "Medium Pen", "stylus_3");
        group.addItem("size3", "Large Pen", "stylus_5");
        mainMenu.realise();
        mainMenu.show();
        scribble = new irex.ScribbleManager();
    }

    public bool draw_expose(Gtk.DrawingArea a, Gdk.EventExpose e) {
        var area = e.area;
        //stdout.printf("expose: %d,%d: %dx%d\n", area.x, area.y, area.width, area.height);
        int x = area.x;
        int y = area.y;
        int width = area.width;
        int height = area.height;
        if (newscribble) {
            a.queue_draw_area(0, 0, drawWidth, drawHeight);
            x = y = 0;
            width = drawWidth;
            height = drawHeight;
            newscribble = false;
        }
        a.window.draw_drawable(a.get_style().fg_gc[Gtk.StateType.NORMAL], 
                pixmap, x, y, x, y , width, height);
        
        return false;
    }

    public bool draw_configure(Gtk.DrawingArea a, Gdk.EventConfigure e) {
        var alloc = a.allocation;
        pixmap = new Pixmap(a.window, alloc.width, alloc.height, -1);
        Gdk.draw_rectangle(pixmap, a.get_style().white_gc, true, 0, 0, 
                alloc.width, alloc.height);
        drawWidth = alloc.width;
        drawHeight = alloc.height;
        return true;
    }

    public bool draw_motion_notify(Gtk.DrawingArea a, Gdk.EventMotion e) {
        double pressure;
        int x, y;
        Gdk.ModifierType state;

        if (e.is_hint) {
            e.window.get_pointer(out x, out y, out state);
        } else {
            x = (int)e.x;
            y = (int)e.y;
            state = e.state;
        }
        Gdk.Event ev = (Gdk.Event)e;
        ev.get_axis(Gdk.AxisUse.PRESSURE, out pressure);
        if (((state & Gdk.ModifierType.BUTTON1_MASK)!=0) && pixmap!=null) {
            scribble.pen_move(x, y); 
            draw_brush(a, e.device.source, x, y, pressure);
        }
        return true;
    }

    public bool draw_button_press(Gtk.DrawingArea a, Gdk.EventButton e) {
        if (e.button == 1 && pixmap!=null) {
            double pressure;
            Gdk.Event ev = (Gdk.Event)e;
            ev.get_axis(Gdk.AxisUse.PRESSURE, out pressure);
            scribble.pen_down((int)e.x, (int)e.y); 
            draw_brush(a, e.device.source, (int)e.x, (int)e.y, pressure);
        }
        return true;
    }

    public bool draw_button_release(Gtk.DrawingArea a, Gdk.EventButton e) {
        if (e.button == 1 && pixmap!=null) {
            double pressure;
            Gdk.Event ev = (Gdk.Event)e;
            ev.get_axis(Gdk.AxisUse.PRESSURE, out pressure);
            scribble.pen_up((int)e.x, (int)e.y); 
            draw_brush(a, e.device.source, (int)e.x, (int)e.y, pressure);
            oldx = -1;
            oldy = -1;
        }
        return true;
    }
                
    public void draw_clear_page() {
        Gdk.draw_rectangle(pixmap, drawingArea.get_style().white_gc, true, 0, 0, drawWidth, drawHeight);
        newscribble = true;
    }

    public void draw_brush(Gtk.DrawingArea a, Gdk.InputSource source, int x, int y, double pressure) {
        if (oldx==-1 && oldy==-1) {
            oldx = x;
            oldy = y;
        } else {
            a.get_style().black_gc.set_line_attributes(pensize, Gdk.LineStyle.SOLID, 
                                                          Gdk.CapStyle.PROJECTING, 
                                                          Gdk.JoinStyle.MITER);
            Gdk.draw_line(pixmap, a.get_style().black_gc, oldx, oldy, x, y);
            newscribble = true;
            oldx = x;
            oldy = y;
        }
    }
    
    /**
     * Create app ui, then inform sysd we're ready
     */
    public void run(string[] args) {
        mainWin = new Gtk.Window(Gtk.WindowType.TOPLEVEL);
        var vbox = new Gtk.VBox(false, 0);
        mainWin.add(vbox);
        vbox.show();

        // Create the drawing area
        drawingArea = new Gtk.DrawingArea();
        vbox.pack_start(drawingArea, true, true, 0);
        drawingArea.show();

        // Signals used to handle backing pixmap
        drawingArea.expose_event += draw_expose;
        drawingArea.configure_event += draw_configure;
        drawingArea.motion_notify_event += draw_motion_notify;
        drawingArea.button_press_event += draw_button_press;
        drawingArea.button_release_event += draw_button_release;

        drawingArea.set_events(Gdk.EventMask.EXPOSURE_MASK
                | Gdk.EventMask.LEAVE_NOTIFY_MASK
                | Gdk.EventMask.BUTTON_PRESS_MASK
                | Gdk.EventMask.BUTTON_RELEASE_MASK
                | Gdk.EventMask.POINTER_MOTION_MASK
                | Gdk.EventMask.POINTER_MOTION_HINT_MASK);

        drawingArea.set_extension_events(Gdk.ExtensionMode.CURSOR);

        mainWin.show_all();
        mainWin.destroy += Gtk.main_quit;
        ipc.send_startup_complete();
    }

    /**
     * Implement irex.Application::getMainWindow
     */
    public Gtk.Window getMainWindow() {
        return this.mainWin;
    }

    /**
     * Implement irex.Application::onMenuClick
     */
    public void onMenuClick(string item, string group, string menu, string state) {
        switch (item) {
            case "close": 
                Gtk.main_quit(); 
                break;
            case "clear":
                draw_clear_page();
                break;
            case "size1":
                pensize = 1;
                scribble.pen_set_size(1);
                break;
            case "size2":
                pensize = 3;
                scribble.pen_set_size(3);
                break;
            case "size3":
                pensize = 5;
                scribble.pen_set_size(5);
                break;
        }
    }
        
    /**
     * Implement irex.Application::onFileOpen
     */
    public bool onFileOpen(string filename, out int xid, out string error) { 
        //int my_xid = (int)Gdk.x11_drawable_get_xid(mainWin.window);
        return true; 
    }

    /**
     * Implement irex.Application::onFileClose
     */
    public bool onFileClose(string filename) { 
        return true; 
    }
    
    /**
     * Implement irex.Application::onWindowChange
     */
    public void onWindowChange(int xid, bool activated) {
        if (activated)
            mainMenu.show();
    }

    public void onPrepareUnmount(string device) {}
    public void onUnmounted(string device) {}
    public void onMounted(string device) {}
    public void onPrepareHibernate() {}
    public void onChangedLocale(string locale) {}
    public void onChangedOrientation(string orientation) {}

    /**
     * Main entry point
     */
    static int main (string[] args) {
        Gtk.init (ref args);
        var app = new ScibbleTest();
        app.run(args);
        Gtk.main ();
        return 0;
    }
}
